/*
By Marcus Ruether
3-20-08
Desc: class to determine what kind of lexam a token is
adds it to a linked list.
Input streams are determined from what files are passed into lex_main(FILE *IN, FILE *OUT, FILE *ERR)
Writes valid tokens to OUT and adds to tokenList
Writes junk tokens to ERR and doesnt add them to tokenList
Reads from IN  --- (see lex_main() function
*/
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<stdlib.h>
#include"lexar.h"
#define BUFFER 500


//creates a new token
struct token *new_token()
{
	struct token *t = (struct token *) malloc (sizeof(struct token));
	
	t->next = NULL;
	strcpy(t->strVal, "");
	return t;
}


//creats a new tokenList(singly linked list of tokens)
struct tokenList *new_tokenList()
{
	struct tokenList *tl = (struct tokenList *) malloc(sizeof(struct token));

	tl->head = NULL;
	tl->tail = NULL;

	return tl;
}


//adds token to linked list of tokens(tokenList)
void push_token(struct tokenList *tokenList, struct token *newToken)
{
	if(tokenList->head == NULL)
	{
		tokenList->head = tokenList->tail = newToken;
	}
	else
	{
		tokenList->tail->next = newToken;
		tokenList->tail = newToken;
	}

}

//deletes linked list of tokens
void delete_tokenList(struct tokenList *t)
{
	struct token *pos = t->head;
	struct token *old;
	int i =0;
	while(pos != NULL)
	{
		i++;
		old = pos;
		pos = pos->next;
		free(old);
	}
	free(t);
}

//----------------------------main lex function
void lex_main(FILE *IN, FILE * OUT, FILE *ERR, struct tokenList *Operations [], int numOfOps)
{
	char line[BUFFER];
	int lineNumb = 0;
	int j = 0;
	int tokenCount = 0;
	//create linked list for registers, opcodes, floats, ints
	while(fgetstr(line, BUFFER, IN) != NULL)//reads in line by line all lines in a file
	{
			++lineNumb;
				
			struct tokenList *tl = new_tokenList();
				
			char *tokenized;
			//if the first token of the command is found to be a comment, the rest will also be triggered as comments
			int isComment = 0;

			tokenized = strtok(line," ");
	
			while(tokenized != NULL)//loops through string of values seperated by whitepace, in this case "line"
			{			//see bottom of this loops to see how value is being incremented
				struct token *newToken = new_token();
				int regiValue;
				enum opcode opValue;
				if((strcmp(tokenized, "//") == 0) || (isComment == 1))
				{
					newToken->type = COMMENT;
					strcpy(newToken->strVal, tokenized);
					newToken->lineNumb = lineNumb;
//					fprintf(OUT, "The token is a comment: %s\n", tokenized);
					push_token(tl,newToken);
					isComment = 1;
				}
				//check if reg
				else if((regiValue = is_regi(tokenized)) != -1)
				{
					//add values to token
					newToken->type = REGI; 
					newToken->v.r = regiValue;
					newToken->lineNumb = lineNumb;
					strcpy(newToken->strVal,tokenized);
					//add to regi list
					push_token(tl, newToken);
					//write token to OUT
//					fprintf (OUT, "This token is of type REGISTER and has a value of: %s\n", tokenized );
				}
				//check if op
				else if((opValue = is_opcode(tokenized)) != -1)
				{
					//add values to token
					newToken->type = OPCODE;
					newToken->v.op = opValue;
					newToken->lineNumb = lineNumb;
					strcpy(newToken->strVal,tokenized);
					//add to opcode list
					push_token(tl, newToken);
					//write token to OUT
//					fprintf (OUT, "This token is of type OPCODE and has a value of: %s\n", tokenized );
				}
				//check if int
				else if(is_int(tokenized))
				{
					//add values to token
					newToken->type = INTEGER;
					newToken->v.i = atoi(tokenized);
					newToken->lineNumb = lineNumb;
					strcpy(newToken->strVal,tokenized);
					//add to int list
					push_token(tl, newToken);
					//write token to OUT
//					fprintf (OUT, "This token is of type INT and has a value of: %s\n", tokenized );
				}
				else if(strcmp(tokenized, "") == 0)
				{
					newToken->type = SPACE;
					newToken->lineNumb = lineNumb;
					push_token(tl,newToken);
					Operations[j] = tl;
				}
				//else is junk
				else
				{
					newToken->type = JUNK;
					strcpy(newToken->strVal,tokenized);
					newToken->lineNumb = lineNumb;
					//print to ERROR
//					fprintf(ERR, "The token %s is not a valid type and was found on line %d\n", tokenized, lineNumb);
					push_token(tl,newToken);
				}
				tokenized = strtok(NULL," ");//gets next token from "line" string
				++tokenCount;
			}
			//adds tokenlist to operation array
			
			Operations[j] = tl;	

			++j;
	}
//	fprintf(OUT,"Number of lines %d\nNumber of tokens read in %d\n", lineNumb, tokenCount);
}
//----------------------------end main lex function


// From http://www.siafoo.net/snippet/75
//Last updated by schram  on 2008-03-27 10:47 (ver. 2)
//fgetstr() - mimics behavior of fgets(), but removes new-line
//   character at end of line if it exists
char *fgetstr(char *string, int n, FILE *stream)
{
	char *result;
	result = fgets(string, n, stream);
	if(!result)
		return(result);

	if(string[strlen(string) - 1] == '\n')
		string[strlen(string) - 1] = 0;

	return(string);
}

//accepts a string, checks if it has an int value
int is_int(char *inttest)
{
	int i = 0;	
	//first char can be negative sign		
	if((int)inttest[0] != 45 && ( ((int)inttest[i] < 48) || ((int)inttest[i] > 57) ) )
	{
		return 0;
	}
	++i;
	//loop through array of chars, check that there is a series of digits with a decimal somewhere in it
	for(i; i < (strlen(inttest)); ++i)
	{
		//if char is not between 0-9
		if( ((int)inttest[i] < 48) || ((int)inttest[i] > 57) )//char between 0 - 9	
		{
			return 0;	
		}
	}
	return 1; // is int
}


//accepts a string, checks to see if it has a float value
int is_float(char *floattest)
{	
	int i = 0;
	int numDecimals = 0;
	//loop through array of chars, check that there is a series of digits with a decimal somewhere in it
	for(i; i < (strlen(floattest)); ++i)
	{
		//if char is between 0-9 or is a "." then it is valid
		if( ( ((int)floattest[i] >= 48) && ((int)floattest[i] <= 57) ) || ((int)floattest[i] == 46) )//char between 0 - 9	
		{	
			if((int)floattest[i] == 46)
			{
				numDecimals++;
			}
		}
		else//else invalid
		{
			return 0;
		}
		
	}
	if(numDecimals == 1)//floats must have at least one decimal
	{
		return 1; //is a float
	}
	else
	{
		return 0; // is not a float
	}
}


//pass in a string, if its index number will be returned that will correspond to the register
//on the Virtual Machine, else 0 will be returned
int is_regi(char *regtest)
{
	struct { //hold register names as strings along with the actual register value
		char *name;
		enum regi r;
	} regi_names [] = {
		{"A", A},
		{"R1", R1},
		{"R2", R2},
		{"R3", R3},
		{"R4", R4},
		{"R5", R5},
		{"R6", R6},
		{"R7", R7},
		{"FINISH", A}
	};//keeping the REGIs for the hell of it

	int i = 0;
	while(regi_names[i].name != "FINISH")//loops through all possible registers and checks if current token is equal to any
	{
		if(strcmp(regi_names[i].name,regtest) == 0)//if valid register
		{
			return i;//token is a register, returns number used in VMState to access that register
		}
		i++;
	}
	return -1;//not a register, return -1
}

//pass in a string, if it is an opcode 1 will be returned, else 0 will be returnend
enum opcode is_opcode(char *optest)
{
	struct {//hold opcode names as strings along with their actual value
		char *name;
		enum opcode op;
	} op_names[] = {
		{"ADD", ADD},
		{"SUB", SUB},
		{"MOV", MOV},
		{"MULT", MULT},
		{"DIV", DIV},
		{"JNZ", JNZ},
		{"JNEQ", JNEQ},
		{"JEQ", JEQ},
		{"BR", BR},
		{"JGT", JGT},
		{"JLT", JLT},
		{"GET", GET},
		{"PUT", PUT},
		{"PUTC", PUTC},
		{"FINISH", FINISH}
	};

	int i = 0;	
	while(op_names[i].name != "FINISH")//loops through all possible opcodes to see if current token is equal to any
	{
		if(strcmp(op_names[i].name,optest) == 0)//if valid opcode
		{
			return op_names[i].op;//returns opcode
		}
		i++;
	}
	return -1;//not an opcode, return -1
}



